package com.liuwei.spring.cloud.common.utils;

import com.liuwei.spring.cloud.common.lang.SerializableFunction;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.reflections.Reflections;
import org.reflections.scanners.FieldAnnotationsScanner;
import org.reflections.scanners.MethodAnnotationsScanner;
import org.reflections.scanners.ResourcesScanner;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;

import java.lang.annotation.Annotation;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.*;
import java.net.URL;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author : evan.liu
 * @version V1.0
 * @Description: 通用反射类
 * @date Date : 2022年04月03日 2:37 下午
 */

@SuppressWarnings("ALL")
public class Reflects {
    public static void main(String[] args) {
        Method[] method = Reflects.class.getMethods();
        AtomicReference<Method> taget = new AtomicReference<>();
        Arrays.asList(method).stream().forEach(x->{
            String name = x.getName();
            if(StringUtils.containsIgnoreCase(name,"getAnnotatedValue")){
                taget.set(x);
            }
        });
        Method m = taget.get();
        Class clazz = m.getReturnType();
        // boolean
        // java.lang.Object
        // java.lang.reflect.Field[]
        // java.util.List
        System.out.println(clazz.getCanonicalName());

        // java.util.List<java.lang.reflect.Field>
        Type type = m.getGenericReturnType();
        System.out.println(type.getTypeName());
    }

    /**
     * 获取 clazz 所有字段
     * @param clazz
     * @return
     */
    public static Field[] getChildrenFields(Class<?> clazz){
        return clazz.getDeclaredFields();
    }

    /**
     * 判断 Class 是否为父类
     * @param parent
     * @param child
     * @return
     */
    public static boolean isParentClass(Class<?> parent,Class<?> child){
        return parent.isAssignableFrom(child);
    }

    /**
     * 获取(指定 package) 下 (指定父类的 class) 实体的(指定变量类型) 的所有变量字段
     * @param scanPackage
     * @param classParent
     * @param fieldParent
     * @return
     */
    public static List<Field> getFieldsInPackage(String scanPackage,Class classParent,Class fieldParent){
        List<Field> fields = new ArrayList<>();
        Set<Class<?>> allClass = getSubClass(scanPackage,classParent);
        CollectionUtils.emptyIfNull(allClass).stream().forEach(clazz->{
            Field[] childrenFields = clazz.getDeclaredFields();
            if(ArrayUtils.isNotEmpty(childrenFields)){
                for (int i = 0; i < childrenFields.length; i++) {
                    Field f = childrenFields[i];
                    Class fieldType = f.getType();
                    if(fieldParent.isAssignableFrom(fieldType)){
                        fields.add(f);
                    }
                }
            }
        });
        return fields;
    }

    /**
     * 通过 Class::get 实例方法获取 Field  名称
     * @param function
     * @return
     * @throws Exception
     */
    public static<T> String getFieldName(SerializableFunction<T,?> function) throws Exception{
        Class clazz = getClassFromGetter(function);
        Method method = getMethodFromGetter(function);
        Field field = clazz.getDeclaredField(methodToProperty(method.getName()));
        return field.getName();
    }

    /**
     * 通过 Class::get 实例方法获取 Field  名称
     * @param function
     * @return
     * @throws Exception
     */
    public static<T> Field getFieldFromGetter(SerializableFunction<T,?> function) throws Exception{
        Class clazz = getClassFromGetter(function);
        Method method = getMethodFromGetter(function);
        Field field = clazz.getDeclaredField(methodToProperty(method.getName()));
        return field;
    }

    /**
     * 通过 Class::get 实例方法获取 get 方法名称
     * @param function
     * @return
     * @throws Exception
     */
    public static<T> Method getMethodFromGetter(SerializableFunction<T,?> function) throws Exception{
        Class clazz = getClassFromGetter(function);
        SerializedLambda meta = resolveLambdaMeta(function);
        String methodName =  meta.getImplMethodName();
        Method method = clazz.getDeclaredMethod(methodName);
        return method;
    }

    /**
     * 通过 Class::get 实例方法获取 get 方法名称
     * @param function
     * @return
     * @throws Exception
     */
    public static<T> Class getClassFromGetter(SerializableFunction<T,?> function) throws Exception{
        SerializedLambda meta = resolveLambdaMeta(function);
        String className = meta.getImplClass();
        Class clazz = Class.forName(className.replaceAll("/","."));
        return clazz;
    }

    /**
     * 通过 Class::get 实例方法获取 lambda Meta 信息
     * https://blog.csdn.net/leisurelen/article/details/105980615
     */
    public static <T> SerializedLambda resolveLambdaMeta(SerializableFunction<T,?> func) throws Exception {
        Class clazz = func.getClass();
        Method writeReplace = func.getClass().getDeclaredMethod("writeReplace");
        writeReplace.setAccessible(true);
        Object sl = writeReplace.invoke(func);
        SerializedLambda serializedLambda = (SerializedLambda) sl;
        return serializedLambda;
    }

    /**
     * java 字段名称转数据库 列名
     * Guava -> com.google.common.base.CaseFormat
     * @param filedName
     * @return
     */
    /*public static String lowerCamel2LowerUnderscore(String filedName){
        return CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, filedName);
    }*/

    /**
     * 根据Method方法名获取Property属性名称
     * org.apache.ibatis.reflection.property.PropertyNamer
     * @param name
     * @return
     */
    public static String methodToProperty(String name) {
        if (name.startsWith("is")) {
            name = name.substring(2);
        } else {
            if (!name.startsWith("get") && !name.startsWith("set")) {
                throw new RuntimeException("Error parsing property name '" + name + "'.  Didn't start with 'is', 'get' or 'set'.");
            }
            name = name.substring(3);
        }
        if (name.length() == 1 || name.length() > 1 && !Character.isUpperCase(name.charAt(1))) {
            name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1);
        }
        return name;
    }

    /**
     * 获取指定包下的所有子类class
     * @param scanPackage
     * @param parentClass
     * @return
     */
    public static Set<Class<?>> getSubClass(String scanPackage,Class parentClass){
        Reflections reflections = new Reflections(scanPackage);
        Set<Class<?>> impls = reflections.getSubTypesOf(parentClass);
        return impls;
    }

    /**
     * 根据包名获取包的指定子类
     * @param packageName 指定包名
     * @param packageName 指定父类名称
     * @throws Exception
     */
    /**
     *  Reflections reflections = new Reflections(new ConfigurationBuilder()
     * .forPackages("com.boothsun.reflections") // 指定路径URL
     * .addScanners(new SubTypesScanner()) // 添加子类扫描工具
     * .addScanners(new FieldAnnotationsScanner()) // 添加 属性注解扫描工具
     * .addScanners(new MethodAnnotationsScanner() ) // 添加 方法注解扫描工具
     * .addScanners(new MethodParameterScanner() ) // 添加方法参数扫描工具
     */
    public static Set<Class<?>> getSubClasses(String packageName,Class clazz) throws Exception {
        List<ClassLoader> classLoadersList = new LinkedList<ClassLoader>();
        classLoadersList.add(ClasspathHelper.contextClassLoader());
        classLoadersList.add(ClasspathHelper.staticClassLoader());

        Collection<URL> scannerPkg = ClasspathHelper.forPackage(packageName);

        ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
        /* don't exclude Object.class */
        SubTypesScanner subTypesScanner = new SubTypesScanner(false);
        // 指定扫描包
        FilterBuilder filter = new FilterBuilder().include(FilterBuilder.prefix(packageName));
        Reflections reflections = new Reflections(configurationBuilder
                .setScanners(subTypesScanner, new ResourcesScanner())
                .setUrls(ClasspathHelper.forClassLoader(classLoadersList.toArray(new ClassLoader[0])))
                .filterInputsBy(filter));
        Set<Class<? extends Object>> allClasses = reflections.getSubTypesOf(clazz);
        return allClasses;
    }

    /**
     * 获取 Package 下带指定注解的 Field
     * @param packageName
     * @param annotationClazz
     * @return
     */
    public static Set<Field> getAnnotationField(String packageName,Class annotationClazz){
        Reflections reflections = new Reflections(new ConfigurationBuilder()
                        .setUrls(ClasspathHelper.forPackage(packageName))
                        .addScanners(new FieldAnnotationsScanner()));

        Set<Field> annotated = reflections.getFieldsAnnotatedWith(annotationClazz);
        return annotated;
    }

    // 所有带RequestMapping注解的方法

    /**
     * 获取 Package 下带指定注解的 Method
     * 如: 所有带RequestMapping注解的方法
     */
    public static Set<Method> getAnnotationMethod(String packageName,Class annotationClazz){
        Reflections reflections = new Reflections(new ConfigurationBuilder()
                        .setUrls(ClasspathHelper.forPackage(packageName))
                        .addScanners(new MethodAnnotationsScanner()));

        Set<Method> annotatedWith = reflections.getMethodsAnnotatedWith(annotationClazz);
        return annotatedWith;
    }

    /**
     * 获取 Package 下带指定注解的 Class
     * @param packageName
     * @param annotationClazz
     * @return
     */
    public static Set<Class<?>> getAnnotationType(String packageName,Class<? extends Annotation> annotationClazz){
        Reflections  reflections = new Reflections(packageName);
        Set<Class<?>> classes = reflections.getTypesAnnotatedWith(annotationClazz);
        return classes;
    }

    public static Object getAnnotatedValue(Field f, Class annotationClazz, String key) throws Exception {
        Annotation annotation = f.getAnnotation(annotationClazz);
        if(annotation == null){
            throw new NoSuchFieldException("can not find annotation on field");
        }
        InvocationHandler invocationHandler = Proxy.getInvocationHandler(annotation);
        // private final Map<String, Object> memberValues 要打开权限
        Field memberValuesField = invocationHandler.getClass().getDeclaredField("memberValues");
        memberValuesField.setAccessible(true);
        // 获取 memberValues
        Map<String, Object> memberValues = (Map<String, Object>) memberValuesField.get(invocationHandler);
        // 修改 value 属性值 这里修改的是@Excel(name = "姓名"); name是key
        return memberValues.get(key);
    }

    public static void setAnnotatedValue(Field f,Class annotationClazz,String key,String value) throws Exception {
        Annotation annotation = f.getAnnotation(annotationClazz);
        if(annotation == null){
            throw new NoSuchFieldException("can not find annotation on field");
        }
        InvocationHandler invocationHandler = Proxy.getInvocationHandler(annotation);
        // private final Map<String, Object> memberValues 要打开权限
        Field memberValuesField = invocationHandler.getClass().getDeclaredField("memberValues");
        memberValuesField.setAccessible(true);
        // 获取 memberValues
        Map memberValues = (Map) memberValuesField.get(invocationHandler);
        // 修改 value 属性值 这里修改的是@Excel(name = "姓名"); name是key
        memberValues.put(key, value);
    }

}
